If the nonce is set to All Zeros then the first block of the encryption can leak the authentication key.
This is because the Authentication key is the encryption of zero data. Using XOR you can recover the keystream if you know the plaintext.
This does depend on how GCM creates its IVs. If the IV starts off at 1 then it is possible to over flow the counter and get all IV and counter to 0.
Example:
message=b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"#Generate the GCM Key using the encryption_key and null data
auth_key=aes.ecb_encryption(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")gmac=GMAC(auth_key)# IV is 96 bytes
iv=bytes.fromhex("000000000000000000000000")#Encrypt plaintext in Counter Mode
ciphertext=aes.ctr_encryption(iv,message)#Use the IV as Additional authenticated data (AAD)
#So if the iv changes then the tag changes
tag_iv=gmac.digest(b"",ciphertext)#Then Encrypt the new data with the
tag=aes.ctr_encryption(tag_iv,tag)print(f"auth_key: {auth_key.hex()}, tag_iv: {tag_iv.hex()}, iv: {iv.hex()}, ciphertext: {ciphertext.hex()}, tag: {tag.hex()}")#auth_key: aa1908ba6ab97a18ea6349b72eb1ba15, tag_iv: 00000000000000000000000000000000, iv: 000000000000000000000000, ciphertext: aa1908ba6ab97a18ea6349b72eb1ba15, tag: d387e6b9293ead8758976e85dd9e064b
Since AES-GCM encrypts the message by XORing it with the output of AES-CTR, a duplicate nonce will result in identical AES-CTR output. If you know a plaintext and the ciphertext of one message then you can decrypt the other message
Duplicate Keystream:
message=b"Cool new message that"message2=b'This is a testMessage'#Initialize AES Key
#encryption_key = os.urandom(16)
encryption_key=bytes.fromhex("bb98a1ffb3ece7a0a284cb5f5cd748ec")aes=AES(encryption_key)#Do AES GCM for message 1
iv, ciphertext, tag=aes_gcm(aes,bytes.fromhex("4ff34dcd6d2738f70bdab5f7"),message)print(f"iv: {iv.hex()}, ciphertext: {ciphertext.hex()}, tag: {tag.hex()}")#iv: 4ff34dcd6d2738f70bdab5f7, ciphertext: 9c4f37ccd0f521bd2dc4437ebe0c1a0b87e909a6dc, tag: 11944bc91a02aaf0b72b3f1b06d6d207
#### Do AES GCM for message 2
#Do AES GCM for message 1
iv2, ciphertext2, tag2=aes_gcm(aes,bytes.fromhex("4ff34dcd6d2738f70bdab5f7"),message2)print(f"iv: {iv2.hex()}, ciphertext: {ciphertext2.hex()}, tag: {tag2.hex()}")#iv: 4ff34dcd6d2738f70bdab5f7, ciphertext: 8b4831d3d0f237ea6c895268be19300bd4ee00a0cd, tag: 9d316f98882a5cf704a2e5d6bdf44474
#### XOR the known plaintext to get the keystream data
keystream=fixedlen_xor(ciphertext,b"Cool new message that")print(f"Keystream: {keystream.hex()}")#Keystream: df2058a0f09b44ca0da9260dcd6d7d6ea79d61c7a8
#### XOR The keystream data to get the unknown message2
plaintext=fixedlen_xor(ciphertext2,keystream)print(f"plaintext: {plaintext}")#plaintext: b'This is a testMessage'
assertplaintext==message2
importosfromaes_libimportAESfromcryptopals_libimportfixedlen_xor,to_blocks,bytes_to_int,int_to_bytesclassGMAC():def__init__(self,authentication_key):self.length=16self.authentication_key=authentication_keydefmult_Galios_Feild(self,y):"""Multiply two polynomials in GF(2^m)/g(w)
g(w) = w^128 + w^7 + w^2 + w + 1
(operands and result bits reflected)"""temp_key=bytes_to_int(self.authentication_key)y=bytes_to_int(y)z=0#print(temp_key,y,z)
whiley&((1<<128)-1):ify&(1<<127):z^=temp_keyy<<=1iftemp_key&1:temp_key=(temp_key>>1)^(0xe1<<120)else:temp_key>>=1returnzdefxor_Mult_with_key(self,p,q):#print("p = {}, q = {}".format(p,q))
test=fixedlen_xor(p,q)#print("xor {}".format(test))
test=self.mult_Galios_Feild(test)test=int_to_bytes(test).rjust(self.length,b'\x00')#print("output {}".format(test))
#print()
returntestdefgLen(self,s):#Get Byte length * 8
return(len(s)<<3).to_bytes(2,byteorder="big")defdigest(self,additional_authenticated_data,input_data):output=b"\x00"*16#Pad additional_authenticated_data
additional_authenticated_data_padded=additional_authenticated_data+bytes((self.length-len(additional_authenticated_data)%self.length)%self.length)#print("Padded Authenticated Data: {}".format(additional_authenticated_data_padded))
#Pad Input Data
input_data_padded=input_data+bytes((self.length-len(input_data)%self.length)%self.length)#print("Input Data: {}".format(input_data_padded))
#For each block of Padded additional_authenticated_data xor and mult with the auth key
foraad_blockinto_blocks(additional_authenticated_data_padded,self.length):output=self.xor_Mult_with_key(output,aad_block)#print("ADD OutputData: {}".format(input_data_padded))
#For each block of Padded input_data xor and mult with the auth key
forinput_blockinto_blocks(input_data_padded,self.length):output=self.xor_Mult_with_key(output,input_block)#Also XOR and mult with the length of the data
len_input=self.gLen(additional_authenticated_data)+self.gLen(input_data)returnself.xor_Mult_with_key(output,len_input.rjust(self.length,b'\x00'))defaes_gcm(aes_obj,iv,message):#Generate the GCM Key using the encryption_key and null data
auth_key=aes.ecb_encryption(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")gmac=GMAC(auth_key)# IV is 96 bytes
ifiv==None:iv=bytes.fromhex("4ff34dcd6d2738f70bdab5f7")#iv = os.urandom(16)
# If tag is 96 bytes then shift and add 1 to make it 128 bits long
# Else GMAC the IV
iflen(iv)==12:tag_iv=iv+b"\x00\x00\x00\x01"else:tag_iv=gmac.digest(iv,b"")#Encrypt plaintext in Counter Mode
ciphertext=aes.ctr_encryption(iv,message)#Use the IV as Additional authenticated data (AAD)
#So if the iv changes then the tag changes
tag=gmac.digest(b"",ciphertext)#Then Encrypt the new data with the
tag=aes.ctr_encryption(tag_iv,tag)#print(f"auth_key: {auth_key.hex()}, tag_iv: {tag_iv.hex()}, iv: {iv.hex()}, ciphertext: {ciphertext.hex()}, tag: {tag.hex()}")
return[iv,ciphertext,tag]if__name__=='__main__':message=b"Cool new message that"#Initialize AES Key
#encryption_key = os.urandom(16)
encryption_key=bytes.fromhex("bb98a1ffb3ece7a0a284cb5f5cd748ec")aes=AES(encryption_key)#Do AES GCM for message 1
iv, ciphertext, tag=aes_gcm(aes,bytes.fromhex("4ff34dcd6d2738f70bdab5f7"),message)print(f"iv: {iv.hex()}, ciphertext: {ciphertext.hex()}, tag: {tag.hex()}")#iv: 4ff34dcd6d2738f70bdab5f7, ciphertext: 9c4f37ccd0f521bd2dc4437ebe0c1a0b87e909a6dc, tag: 11944bc91a02aaf0b72b3f1b06d6d207